/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.fedoraifz.restrequests;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager; //import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.jvnet.ws.wadl.util.StreamDataSource;

public class HttpManagement {

    private static Logger log = Logger.getLogger(HttpManagement.class);

    private ClientConnectionManager httpConnectionManager;

    private HttpCustomParameters customParameters;

    public void setHttpConnectionManager(
	    ClientConnectionManager httpConnectionManager) {
	this.httpConnectionManager = httpConnectionManager;
    }

    public ClientConnectionManager getHttpConnectionManager() {
	return httpConnectionManager;
    }

    public void setCustomParameters(HttpCustomParameters customParameters) {
	this.customParameters = customParameters;
    }

    public HttpCustomParameters getCustomParameters() {
	return customParameters;
    }

    private HttpClient httpClient;

    private String baseURL;

    public String getBaseURL() {
	return baseURL;
    }

    public HttpManagement(String baseURL) {
	this.baseURL = baseURL;
	// Initialize HTTP parameters
	this.customParameters = new HttpCustomParameters(baseURL);
	// Initialize connection manager
	httpConnectionManager = new SingleClientConnManager(
		this.customParameters.getParams(), this.customParameters
			.getSchemeRegistry());
    }

    /**
     * 
     * @return
     */
    public HttpClient getHttpClient() {

	if (this.httpClient == null) {
	    this.httpClient = new DefaultHttpClient(this.httpConnectionManager,
		    this.customParameters.getParams());
	}
	return httpClient;
    }

    /**
     * 
     * @param url
     * @param method
     * @param requestContent
     * @param encoding
     * @param contentType
     * @param headerParameterValues
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    public StreamDataSource putOrPost(String url, String method,
	    Object requestContent, String encoding, String contentType,
	    HashMap<String, Object> headerParameterValues)
	    throws ClientProtocolException, IOException {
	HttpClient httpclient = this.getHttpClient();
	HttpRequestBase httpMethod = null;

	try {
	    if (method.equals("POST")) {
		httpMethod = new HttpPost(url);
		Set<Map.Entry<String, Object>> set = headerParameterValues
			.entrySet();
		Iterator<Map.Entry<String, Object>> i = set.iterator();
		while (i.hasNext()) {
		    Map.Entry<String, Object> entry = (Map.Entry<String, Object>) i
			    .next();
		    httpMethod.addHeader(entry.getKey().toString(), entry
			    .getValue().toString());
		}
	    } else if (method.equals("PUT")) {
		httpMethod = new HttpPut(url);
	    } else {
		throw new IllegalArgumentException(
			"Method parameter must be one of POST or PUT...");
	    }

	    if (requestContent instanceof File) {
		FileBody bin = new FileBody((File) requestContent);
		MultipartEntity reqEntity = new MultipartEntity();
		reqEntity.addPart("param_name", new StringBody("value"));
		reqEntity.addPart(bin.getFilename(), bin);
		((HttpEntityEnclosingRequest) httpMethod).setEntity(reqEntity);
	    } else if (requestContent instanceof String) {
		// Request parameter sent
		if (!requestContent.equals("") && requestContent != null) {
		    StringEntity entity = new StringEntity(
			    (String) requestContent);
		    entity.setContentEncoding(encoding);
		    entity.setContentType(contentType);
		    entity.setChunked(false);
		    ((HttpEntityEnclosingRequest) httpMethod).setEntity(entity);
		}
	    } else {
		log
			.error("Parameter requestContent must be either a String or a File");
		throw new IllegalArgumentException(
			"Parameter requestContent must be either a String or a File");
	    }

	    HttpResponse response = httpclient.execute(httpMethod);
	    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK
		    && response.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) {
		// HANDLE ERRORS
		log.error("Response status line: "
			+ response.getStatusLine().getReasonPhrase());
	    }
	    HttpEntity resEntity = response.getEntity();
	    if (resEntity != null) {
		// String content = EntityUtils.toString(resEntity);
		// log.info(content);
		return new StreamDataSource(resEntity.getContentType()
			.getValue(), resEntity.getContent());
	    } else {
		// NO RESULTS BACK
		return null;
	    }
	} finally {
	    if (httpMethod != null) {
		httpMethod.abort();
	    }
	}
    }

    /**
     * 
     */
    public void closeConnManager() {
	this.httpConnectionManager.shutdown();
    }
}
